package com.kingmed.kmss.bpmn.flow.service.base;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kingmed.kmss.bpmn.flow.utils.FlowableBeanUtils;
import com.kingmed.kmss.common.bo.bpmn.DeploymentBO;
import com.kingmed.kmss.common.bo.bpmn.ProcessDefinitionBO;
import com.kingmed.kmss.common.bo.bpmn.ProcessStarterBO;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * flowable原生service
 */
@Slf4j
@Service
public class BpmNativeServiceImpl extends BpmBaseServiceImpl {

	public List<ProcessDefinitionBO> getProcesssList() {
		List<ProcessDefinition> list = this.getRepositoryService().createProcessDefinitionQuery().list();
		List<ProcessDefinitionBO> result = FlowableBeanUtils.toProcessDefinitionBO(list);
		setDeployTime(result);

		List<ProcessDefinitionBO> sortedResult = result.stream()
				.sorted(Comparator.comparing(ProcessDefinitionBO::getDeployTime).reversed()).collect(Collectors.toList());
		log.info("查询的processDefinition size={}", list.size());
		return sortedResult;
	}

	public List<ProcessDefinitionBO> getLatestProcesssList() {
		List<ProcessDefinition> list = this.getRepositoryService().createProcessDefinitionQuery().latestVersion()
				.list();

		List<ProcessDefinitionBO> result = FlowableBeanUtils.toProcessDefinitionBO(list);
		setDeployTime(result);
		log.info("查询的processDefinition size={}", list.size());
		return result;
	}

	public List<DeploymentBO> getDeployment() {
		List<Deployment> list = this.getRepositoryService().createDeploymentQuery().list();
		List<DeploymentBO> result = FlowableBeanUtils.toDeploymentBO(list);
		log.info("查询的processDefinition size={}", list.size());
		return result;
	}

	public void setDeployTime(List<ProcessDefinitionBO> processList) {
		// get deployment deploy time
		processList.forEach(process -> {
			DeploymentBO deploy = getDeploymentById(process.getDeploymentId());

			if (deploy != null) {
				process.setDeployTime(deploy.getDeployTime());
			}

		});
	}

	public DeploymentBO getDeploymentById(String id) {
		Deployment deployEntity = this.getRepositoryService().createDeploymentQuery().deploymentId(id).singleResult();
		if (deployEntity == null) {
			return null;
		}
		return FlowableBeanUtils.toDeploymentBO(deployEntity);
	}

	public int deleteAllDeployments() {
		List<DeploymentBO> deploys = getDeployment();

		if (deploys != null) {
			deploys.forEach(d -> {
				getRepositoryService().deleteDeployment(d.getId(), true);
				
			});

			return deploys.size();
		}

		return 0;
	}

	@Transactional
	public boolean deleteDeployProcessById(String deployId) {

		DeploymentBO deploy = getDeploymentById(deployId);
		if (deploy == null) {
			return false;
		}
		getRepositoryService().deleteDeployment(deployId, true);

		return true;
	}

	public List<ProcessDefinition> findProcessInstanceByKey(ProcessDefinitionBO definitionBO) {
		List<ProcessDefinition> list = this.getRepositoryService().createProcessDefinitionQuery()
				.processDefinitionKey(definitionBO.getProcessKey()).list();
		log.info("查询的processDefinition size={}", list.size());
		return list;
	}

	public ProcessInstance startProcessInstanceByKey(ProcessStarterBO startBO) {
		ProcessInstance processInstance = this.getRuntimeService().startProcessInstanceByKey(startBO.getProcDefKey());
		log.info("新创建的processInstance id={}", processInstance.getId());
		return processInstance;
	}

	public List<HistoricTaskInstance> findHistoricTaskInstanceByProcessInstanceId(String processInstanceId) {
		return this.getHistoryService().createHistoricTaskInstanceQuery().processInstanceId(processInstanceId)
				.orderByHistoricTaskInstanceStartTime().asc().list();
	}

	public Page<HistoricTaskInstance> findMyDoneTasks(Page<HistoricTaskInstance> page, String userId,
			List<String> processDefinitionIds) {
		int startIndex = page.getCurrent() <= 0 ? 0 : (int) ((page.getCurrent() - 1) * page.getSize());
		long count = this.getHistoryService().createHistoricTaskInstanceQuery().taskAssignee(userId)
				.processDefinitionKeyIn(processDefinitionIds).count();
		List<HistoricTaskInstance> historicTaskInstances = this.getHistoryService().createHistoricTaskInstanceQuery()
				.taskAssignee(userId).processDefinitionKeyIn(processDefinitionIds).orderByHistoricTaskInstanceEndTime()
				.desc().listPage(startIndex, 10);
		return page.setRecords(historicTaskInstances).setTotal(count);
	}

	/**
	 * 高级特性，task动态跳转
	 *
	 * @param processInstanceId
	 * @param fromTaskDefinitionId
	 * @param toTaskDefinitionId
	 */
	public void moveToTask(String processInstanceId, String fromTaskDefinitionId, String toTaskDefinitionId) {
		// Task动态跳转，数据库会新建一个task实例，原有任务is_delete
		this.getRuntimeService().createChangeActivityStateBuilder().processInstanceId(processInstanceId)
				.moveActivityIdTo(fromTaskDefinitionId, toTaskDefinitionId).changeState();
	}

	public HistoricTaskInstance findHistoryTask(String taskId) {
		return this.getHistoryService().createHistoricTaskInstanceQuery().taskId(taskId).singleResult();

	}

	public List<HistoricIdentityLink> findHistoryTaskIdentityLink(String taskId) {
		return this.getHistoryService().getHistoricIdentityLinksForTask(taskId);
	}

	/**
	 * 只查询group id
	 *
	 * @param taskId
	 * @return
	 */
	public String findHistoryTaskIdentityLinkGroupId(String taskId) {
		List<HistoricIdentityLink> links = this.getHistoryService().getHistoricIdentityLinksForTask(taskId);
		List<String> groupIds = new ArrayList<>();
		for (HistoricIdentityLink link : links) {
			if (link.getType().equals("candidate")) {
				groupIds.add(link.getGroupId());
			}
		}

		return StrUtil.join(",", groupIds);
	}

	public List<Task> findActiveTasksByProcessInstanceId(String processInstanceId) {
		return this.getTaskService().createTaskQuery().processInstanceId(processInstanceId).list();
	}

	public List<TaskEntity> findActiveTaskEntityByProcessInstanceId(String processInstanceId) {
		List<TaskEntity> entities = new ArrayList<>();
		List<Task> tasks = this.getTaskService().createTaskQuery().processInstanceId(processInstanceId).list();
		for (Task task : tasks) {
			TaskEntity entity = (TaskEntity) task;
			entities.add(entity);
		}
		return entities;
	}

}
